import org.mybatis.generator.api.CommentGenerator;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.XmlElement;

import java.util.*;

/**
 * @program: generatorSqlmapCustom
 * @description: 插件接入类
 * @author: 顾小皓
 * @create: 2020-07-18 10:51
 **/
public class PaginationPlugin extends PluginAdapter {
    //表注释
    public static Map<String,String> tableRemarks = new HashMap<>();
    //保存需要生成的表名
    public static List<String> tableName = new ArrayList<>();
    //保存需要生成的表数据
    public static Map<String, List<TableFieldsModel>> tablesFields = new HashMap<>();
    //保存需要导入的包路径
    public static Map<String,List<String>> mapFullyQualifiedNames = new HashMap<>();

    private FullyQualifiedJavaType serializable;
    private FullyQualifiedJavaType gwtSerializable;
    private boolean addGWTInterface;
    private boolean suppressJavaInterface;

    @Override
    public void setProperties(Properties properties) {
        super.setProperties(properties);
        addGWTInterface = Boolean.valueOf(properties.getProperty("addGWTInterface")); //$NON-NLS-1$
        suppressJavaInterface = Boolean.valueOf(properties.getProperty("suppressJavaInterface")); //$NON-NLS-1$
    }

    public PaginationPlugin() {
        super();
        serializable = new FullyQualifiedJavaType("java.io.Serializable"); //$NON-NLS-1$
        gwtSerializable = new FullyQualifiedJavaType("com.google.gwt.user.client.rpc.IsSerializable"); //$NON-NLS-1$
    }

    /**
     * 给example添加序列化和分页设置
     *
     * @param topLevelClass
     * @param introspectedTable
     * @return
     */
    @Override
    public boolean modelExampleClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        addLimit(topLevelClass, introspectedTable, "limitStart");
        addLimit(topLevelClass, introspectedTable, "limitEnd");
        makeSerializable(topLevelClass, introspectedTable);
        return super.modelExampleClassGenerated(topLevelClass, introspectedTable);
    }

    /**
     * 给实体类增加序列化设置
     *
     * @param topLevelClass
     * @param introspectedTable
     * @return
     */
    @Override
    public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        makeSerializable(topLevelClass, introspectedTable);
        //获取所有表名
        String shortName = topLevelClass.getType().getShortName();
        tableName.add(shortName);
        return super.modelBaseRecordClassGenerated(topLevelClass, introspectedTable);
    }

    @Override
    public boolean sqlMapSelectByExampleWithoutBLOBsElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
        XmlElement isNotNullElement = new XmlElement("if");
        isNotNullElement.addAttribute(new Attribute("test", "limitStart != null and limitStart >= 0"));
        isNotNullElement.addElement(new TextElement("limit ${limitStart},${limitEnd}"));
        element.addElement(isNotNullElement);
        return super.sqlMapSelectByExampleWithoutBLOBsElementGenerated(element, introspectedTable);
    }

    private void makeSerializable(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        if (addGWTInterface) {
            topLevelClass.addImportedType(gwtSerializable);
            topLevelClass.addSuperInterface(gwtSerializable);
        }

        if (!suppressJavaInterface) {
            topLevelClass.addImportedType(serializable);
            topLevelClass.addSuperInterface(serializable);

            Field field = new Field();
            field.setFinal(true);
            field.setInitializationString("1L");
            field.setName("serialVersionUID");
            field.setStatic(true);
            field.setType(new FullyQualifiedJavaType("long"));
            field.setVisibility(JavaVisibility.PRIVATE);
            context.getCommentGenerator().addFieldComment(field, introspectedTable);

            topLevelClass.addField(field);
        }
    }

    private void addLimit(TopLevelClass topLevelClass, IntrospectedTable introspectedTable, String name) {
        CommentGenerator commentGenerator = context.getCommentGenerator();
        //添加字段
        Field field = new Field();
        field.setVisibility(JavaVisibility.PROTECTED);
        field.setType(FullyQualifiedJavaType.getIntInstance());
        field.setName(name);
        field.setInitializationString("-1");
        commentGenerator.addFieldComment(field, introspectedTable);
        topLevelClass.addField(field);
        char c = name.charAt(0);
        //将首字母转为大写
        String camel = Character.toUpperCase(c) + name.substring(1);
        Method method = new Method();
        method.setVisibility(JavaVisibility.PUBLIC);
        method.setName("set" + camel);
        //如果是分页参数 就重命名一下下
        method.addParameter(new Parameter(FullyQualifiedJavaType.getIntInstance(), getName(name)));
        //添加分页计算  省得入参时候每次都写一遍
        if (name.equals("limitStart")){
            method.addParameter(new Parameter(FullyQualifiedJavaType.getIntInstance(), "rows"));
            method.addBodyLine("page = (page - 1) * rows;");
        }
        method.addBodyLine("this." + name + "=" + getName(name) + ";");
        commentGenerator.addGeneralMethodComment(method, introspectedTable);
        topLevelClass.addMethod(method);
        method = new Method();
        method.setVisibility(JavaVisibility.PUBLIC);
        method.setReturnType(FullyQualifiedJavaType.getIntInstance());
        method.setName("get" + camel);
        method.addBodyLine("return " + name + ";");
        commentGenerator.addGeneralMethodComment(method, introspectedTable);
        topLevelClass.addMethod(method);
    }
    /**
     * 获取各种表信息
     * @param var1
     * @param var2
     * @param var3
     * @param var4
     * @param var5
     * @return
     */
    @Override
    public boolean modelFieldGenerated(Field var1, TopLevelClass var2, IntrospectedColumn var3, IntrospectedTable var4, ModelClassType var5) {
        /**
         * 获取所需全部数据
         */
        //获取所有表名
        String tableName = var2.getType().getShortName();
        //字段名
        String javaProperty = var3.getJavaProperty();
        //字段描述
        String remarks = var3.getRemarks();
        //数据库的字段数据类型
        String jdbcTypeName = var3.getJdbcTypeName();
        FullyQualifiedJavaType fullyQualifiedJavaType = var3.getFullyQualifiedJavaType();
        //字段数据类型
        String shortName = fullyQualifiedJavaType.getShortName();
        //字段数据类型包路径
        String fullyQualifiedName = fullyQualifiedJavaType.getFullyQualifiedName();
        //加入map
        isImport(tableName,fullyQualifiedName);
        /**
         * 先判断map里有没有这个表 如果有就直接提取出来赋值 如果没有就直接put进去
         */
        //如果是id的话就放过
        if (!javaProperty.equals("id")) {
            List<TableFieldsModel> tableFieldsModelList = tablesFields.get(tableName);
            TableFieldsModel tableFieldsModel;
            if (tableFieldsModelList != null && tableFieldsModelList.size() > 0) {
                //放入集合
                tableFieldsModel = new TableFieldsModel();
                tableFieldsModel.setFieldsName(javaProperty);
                tableFieldsModel.setFieldType(shortName);
                tableFieldsModel.setFieldCommon(remarks);
                tableFieldsModel.setImportPackage(fullyQualifiedName);
                tableFieldsModelList.add(tableFieldsModel);
            } else {
                tableFieldsModelList = new ArrayList<>();
                tableFieldsModel = new TableFieldsModel();
                tableFieldsModel.setFieldsName(javaProperty);
                tableFieldsModel.setFieldType(shortName);
                tableFieldsModel.setFieldCommon(remarks);
                tableFieldsModel.setImportPackage(fullyQualifiedName);
                //创建集合
                tableFieldsModelList.add(tableFieldsModel);
                //放入map
                tablesFields.put(tableName, tableFieldsModelList);
                {
                    //获取表注释
                    String tableRemark = var4.getRemarks();
                    //放入该表的注释
                    tableRemarks.put(tableName,tableRemark);
                }
            }
        }
        return super.modelFieldGenerated(var1, var2, var3, var4, var5);
    }

    /**
     * 生成dao
     */
    @Override
    public boolean clientGenerated(Interface interfaze,TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        //必须有主键
        if (introspectedTable.getPrimaryKeyColumns() == null || introspectedTable.getPrimaryKeyColumns().isEmpty()) {
            String tableName = introspectedTable.getFullyQualifiedTableNameAtRuntime();
            throw new RuntimeException(tableName+" 表没设置主键");
        }

        //主键默认采用java.lang.String
        FullyQualifiedJavaType fqjt = new FullyQualifiedJavaType("BaseMapper<"
                + introspectedTable.getBaseRecordType() + ","
                + introspectedTable.getExampleType() + ","
                + TemplatesEmus.getPrimaryKeyTypePath() + ">");
        //这个是import包的方法
        FullyQualifiedJavaType imp = new FullyQualifiedJavaType(TemplatesEmus.project+TemplatesEmus.BaseMapper+".BaseMapper");
        /**
         * 添加 extends MybatisBaseMapper
         */
        interfaze.addSuperInterface(fqjt);

        /**
         * 添加 MybatisBaseMapper;
         */
        interfaze.addImportedType(imp);
        /**
         * 添加@Mapper注解
         */
        if (introspectedTable.getTargetRuntime() == IntrospectedTable.TargetRuntime.MYBATIS3) {
            interfaze.addImportedType(new FullyQualifiedJavaType("org.apache.ibatis.annotations.Mapper"));
            interfaze.addAnnotation("@Mapper");
        }
        /**
         * 删掉没用的
         */
        Set<FullyQualifiedJavaType> importedTypes = interfaze.getImportedTypes();
        FullyQualifiedJavaType remove1 = new FullyQualifiedJavaType("java.util.List");
        FullyQualifiedJavaType remove2 = new FullyQualifiedJavaType("org.apache.ibatis.annotations.Param");
        Set<FullyQualifiedJavaType> newSet = new TreeSet<>(importedTypes);
        newSet.remove(remove1);
        newSet.remove(remove2);
        //清空 重新添加
        importedTypes.clear();
        interfaze.addImportedTypes(newSet);
        /**
         * 清空没用的方法
         */
        interfaze.getMethods().clear();
        return true;
    }
    /**
     * 判断是否需要加入
     * @param fullyQualifiedName
     */
    private void isImport(String tableName,String fullyQualifiedName){
        switch (fullyQualifiedName){
            case "java.lang.Long":
            case "java.lang.Integer":
            case "java.lang.String":
            case "byte[]":
                break;
            default:
                List<String> fullyQualifiedNames = mapFullyQualifiedNames.get(tableName);
                if (fullyQualifiedNames!=null&&fullyQualifiedNames.size()>0){
                    fullyQualifiedNames.add(fullyQualifiedName);
                }else {
                    ArrayList<String> list = new ArrayList<>();
                    list.add(fullyQualifiedName);
                    mapFullyQualifiedNames.put(tableName,list);
                }
        }
    }

    /**
     * 分页方法名转换工具
     * @param name
     * @return
     */
    private String getName(String name){
        return (name.equals("limitStart") ? "page" : name.equals("limitEnd") ? "rows":name);
    }
    @Override
    public boolean validate(List<String> list) {
        return true;
    }
}